//
//  WKWebViewController.swift
//  ZhiTing
//
//  Created by iMac on 2021/3/23.
//

import Alamofire
import CoreTelephony
import UIKit
import WebKit
import Photos
import HandyJSON

class WKWebViewController: BaseViewController {
    var link: String
    var webViewTitle: String?
    
    /// 配网设备model
    var commonDevice : CommonDevice?
    
    /// blufi 配网工具
    lazy var bluFiTool = BluFiTool()
    
    /// soft ap配网工具
    lazy var softAPTool = SoftAPTool()
    
    /// 提供给h5调用的websocket
    var ztWebsocket: ZTWebSocket?
    
    #if !(targetEnvironment(simulator))
    /// 提供给摄像头声波配网
    let soundWaveSender = VSSoundWaveSender()
    #endif
    
    /// 蓝牙门锁工具类
    var doorLockUtil: DoorLockUtil?

    ///是否内嵌页面
    var isEmbeddedPage = false
    
    #if !(targetEnvironment(simulator))
    //摄像机工具
    var cameraTool: CameraTool?
    #endif
    
    var applicationNameForUserAgent: String? {
        return "zhitingua-iOS"
    }
    
    init(linkEnum: LinkEnum) {
        self.link = linkEnum.link
        super.init()
    }

    init(link:String) {
        /// 处理编码问题
        self.link = link
        super.init()
    }
        
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    var webView: WKWebView!
    var eventHandler:WKEventHandlerSwift!
    
    private lazy var progress: UIProgressView = {
        let progres = UIProgressView.init(progressViewStyle: .default)
        progres.frame = CGRect(x: 0, y: 0, width: Screen.screenWidth, height: 1.5)
        progres.progress = 0
        progres.progressTintColor = .custom(.blue_2da3f6)
        progres.trackTintColor = UIColor.clear
        return progres
    }()
    
    deinit {
        ztWebsocket = nil
    }

    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupWebView()
        
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }
    
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
    }
    
   

    private func setupWebView() {
        eventHandler = WKEventHandlerSwift(webView, self)
        let config = WKWebViewConfiguration()
        config.preferences = WKPreferences()
        config.preferences.minimumFontSize = 10
        config.preferences.javaScriptEnabled = true
        config.preferences.javaScriptCanOpenWindowsAutomatically = true
        config.processPool = WKProcessPool()
        config.applicationNameForUserAgent = applicationNameForUserAgent
        
        
        config.userContentController = WKUserContentController()
        /// 注入JS (现已改为前端自行实现相关部分JS方法)
//        let usrScript:WKUserScript = WKUserScript.init(source: WKEventHandlerSwift.handleJS(), injectionTime: .atDocumentStart, forMainFrameOnly: true)
//        config.userContentController.addUserScript(usrScript)
        config.userContentController.add(self.eventHandler, name: WKEventHandlerNameSwift)
        
        webView = WKWebView(frame: .zero, configuration: config)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        webView.allowsBackForwardNavigationGestures = true
        webView.scrollView.contentInsetAdjustmentBehavior = .never
        webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
        
        eventHandler.webView = webView
        
        view.addSubview(webView)
        webView.addSubview(progress)
        
        if isEmbeddedPage {
            webView.snp.makeConstraints { (make) in
                make.top.equalToSuperview()
                make.left.right.bottom.equalToSuperview()
            }
        }else{
            webView.snp.makeConstraints { (make) in
                make.top.equalToSuperview().offset(Screen.k_nav_height)
                make.left.right.bottom.equalToSuperview()
            }
        }

        progress.snp.makeConstraints { (make) in
            make.top.left.right.equalToSuperview()
            make.height.equalTo(1.5)
        }
        
        loadRequest()
    
    }
    
    func loadRequest() {
        if let linkURL = URL(string: link) {
            let request = URLRequest(url: linkURL, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData)
            webView.load(request)
           
        }
    }
    
    
    override func navPop() {
        if webView.canGoBack {
            if let item = webView.backForwardList.backList.first {
                webView.go(to: item)
            }
        }else{
            navigationController?.popViewController(animated: true)
        }
        
    }
    
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        let alert = UIAlertController.init(title: "alert", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction.init(title: "确定", style: .default, handler: { (_ acton:UIAlertAction) in
            completionHandler()
        }))
        self.present(alert, animated: true, completion: nil)
    }
}


extension WKWebViewController: WKNavigationDelegate {
    func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
        webView.reload()
    }

    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        progress.isHidden = false
        self.showLoadingView()
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.hideLoadingView()
        if let webViewTitle = webViewTitle, webViewTitle != "" {
            navigationItem.title = webViewTitle
        }

    }
    
    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        progress.isHidden = true
        progress.progress = 0
        self.hideLoadingView()
    }
    
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if navigationAction.targetFrame == nil {
            webView.load(navigationAction.request)
        }
        if #available(iOS 14.5, *) {
            navigationAction.shouldPerformDownload ? decisionHandler(.download) : decisionHandler(.allow)
        } else {
            decisionHandler(.allow)
        }
        
    }
    
    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if let serverTrust = challenge.protectionSpace.serverTrust {
            let credential = URLCredential(trust: serverTrust)
            challenge.sender?.use(credential, for: challenge)
            // 证书校验通过
            completionHandler(.useCredential, credential)
            return
        }

        completionHandler(.performDefaultHandling, nil)
    }
    
   
    

}

//@available(iOS 14.5, *)
//extension WKWebViewController: WKDownloadDelegate {
//    func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
//        if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
//            completionHandler(url.appendingPathComponent(suggestedFilename))
//            showToast(string: "开始下载")
//        }
//    }
//
//    func downloadDidFinish(_ download: WKDownload) {
//        ZTConsole.log("downloadDidFinish")
//        showToast(string: "下载成功,已保存至\"文件\"")
//
//    }
//
//
//
//    func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) {
//        ZTConsole.log("download failed: \(error.localizedDescription)")
//        showToast(string: "下载失败")
//    }
//
//    func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
//        if let req = download.originalRequest {
//            Task {
//                let download = await webView.startDownload(using: req)
//                download.delegate = self
//            }
//
//        }
//    }
//
//    func download(_ download: WKDownload, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
//        if let serverTrust = challenge.protectionSpace.serverTrust {
//            let credential = URLCredential(trust: serverTrust)
//            challenge.sender?.use(credential, for: challenge)
//            // 证书校验通过
//            completionHandler(.useCredential, credential)
//            return
//        }
//
//        completionHandler(.performDefaultHandling, nil)
//    }
//}


extension WKWebViewController: WKUIDelegate {
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "estimatedProgress" {
            progress.alpha = 1.0
            let animal = webView.estimatedProgress > Double(progress.progress)
            progress.setProgress(Float(webView.estimatedProgress), animated: animal)
            
            if webView.estimatedProgress >= 1.0 {
                UIView.animate(withDuration: 0.3, delay: 0.3, options: .curveEaseOut, animations: {
                    self.progress.alpha = 0
                }) { (finished) in
                    self.progress.setProgress(0, animated: false)
                    self.hideLoadingView()
                }
            }
        }
    }
    
}

//MARK: WKEventHandlerProtocol
extension WKWebViewController: WKEventHandlerProtocol {
    
    
    /// 注册到JS的方法
    /// - Parameters:
    ///   - funcName: 方法名称
    ///   - params: 方法参数
    ///   - callback: 方法回调
    func nativeHandle(funcName: inout String!, params: Dictionary<String, Any>?, callback: ((Any?) -> Void)?) {
        ZTConsole.log("WKWebView调用原生方法: \(funcName ?? "unknown")")
        
        if funcName == "networkType" {
            networkType(callBack: callback)
        } else if funcName == "setTitle" {
            setTitle(params: params ?? [:])
        } else if funcName == "getUserInfo" {
            getUserInfo(callBack: callback)
        } else if funcName == "isApp" {
            isApp(callBack: callback)
        } else if funcName == "isProfession" {
            isProfession(callBack: callback)
        } else if funcName == "connectDeviceHotspot" {
            connectDeviceHotspot(params: params, callBack: callback)
        } else if funcName == "createDeviceByHotspot" {
            createDeviceByHotspot(params: params, callBack: callback)
        } else if funcName == "connectDeviceByHotspot" {
            connectDeviceByHotspot(params: params, callBack: callback)
        } else if funcName == "connectNetworkByHotspot" {
            connectNetworkByHotspot(params: params, callBack: callback)
        } else if funcName == "connectDeviceByBluetooth" {
            connectDeviceByBluetooth(params: params, callBack: callback)
        } else if funcName == "connectNetworkByBluetooth" {
            connectNetworkByBluetooth(params: params, callBack: callback)
        } else if funcName == "getDeviceInfo" {
            getDeviceInfo(callback: callback)
        } else if funcName == "getConnectWifi" {
            getConnectWifi(callback: callback)
        } else if funcName == "toSystemWlan" {
            toSystemWlan()
        } else if funcName == "getSystemWifiList" {
            getSystemWifiList(callback: callback)
        } else if funcName == "getSocketAddress" {
            getSocketAddress(callback: callback)
        } else if funcName == "connectSocket" {
            connectSocket(params: params, callBack: callback)
        } else if funcName == "sendSocketMessage" {
            sendSocketMessage(params: params, callback: callback)
        } else if funcName == "onSocketOpen" {
            onSocketOpen(callback: callback)
        } else if funcName == "onSocketMessage" {
            onSocketMessage(callback: callback)
        } else if funcName == "onSocketError" {
            onSocketError(callback: callback)
        } else if funcName == "onSocketClose" {
            onSocketClose(callback: callback)
        } else if funcName == "closeSocket" {
            closeSocket(callback: callback)
        } else if funcName == "registerDeviceByHotspot" {
            registerDeviceByHotspot(callBack: callback)
        } else if funcName == "registerDeviceByBluetooth" {
            registerDeviceByBluetooth(callBack: callback)
        } else if funcName == "rotorDevice" {
            rotorDevice(params: params)
        } else if funcName == "getLocalhost" {
            getLocalhost(callBack: callback)
        } else if funcName == "rotorDeviceSet" {
            rotorDeviceSet(params: params)
        } else if funcName == "openScan" {
            openScan(callBack: callback)
        } else if funcName == "sendSoundWaves" {
            sendSoundWaves(params: params)
        } else if funcName == "registerDeviceBySoundWaves" {
            registerDeviceBySoundWaves(params: params, callback: callback)
        } else if funcName == "getBluetoothDevices" {
            getBluetoothDevices(params: params, callback: callback)
        } else if funcName == "connectBluetoothDevice" {
            connectBluetoothDevice(params: params, callback: callback)
        } else if funcName == "sendMessageByBluetooth" {
            sendMessageByBluetooth(params: params, callback: callback)
        } else if funcName == "savePhoto" {
            savePhoto(callback: callback)
        } else if funcName == "getBluetoothState" {
            getBluetoothState(params: params, callback: callback)
        } else if funcName == "toDevicePosition" {
            toDevicePosition(params: params)
        } else if funcName == "getLockLogsRealtime" {
            getLockLogsRealtime(callback: callback)
        } else if funcName == "stopSendSoundWaves" {
            stopSendSoundWaves()
        } else if funcName == "goToCamera" {
            goToCamera(params: params)
        } else if funcName == "getBluetoothSupportDevices" {
            getBluetoothSupportDevices(callback: callback)
        } else if funcName == "associateBleDevice" {
            associateBleDevice(params: params, callback: callback)
        } else if funcName == "getPluginConfig" {
            getPluginConfig(params: params, callback: callback)
        } else if funcName == "downloadFile" {
            downloadFile(params: params)
        } else if funcName == "scanDeviceByModel" {
            scanDeviceByModel(params: params, callBack: callback)
        } else if funcName == "toNativePage" {
            self.jumpToHomeVC(params: params)
        }
        
    }
    
    @objc func stopSendSoundWaves(){
        #if !(targetEnvironment(simulator))
        soundWaveSender.stopPlaying()
        #endif
    }
    
    @objc func sendSoundWaves(params: Dictionary<String,Any>?){
        guard let params = params,
              let bssid = params["BSSID"] as? String,
              let pwd = params["wifiPass"] as? String
        else {
            return
        }
        #if !(targetEnvironment(simulator))
        soundWaveSender.playWiFiMac(bssid, password: pwd, userId: "10000", playCount: 5)
        #endif
    }
    
    
    //配网成功后，尝试连接摄像头，连接成功后重置密码并注册
    @objc func registerDeviceBySoundWaves(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        guard let params = params,
              let id = params["ID"] as? String,
              let model = params["model"] as? String,
              let manufacturer = params["manufacturer"] as? String
        else {
            return
        }
        #if !(targetEnvironment(simulator))
        soundWaveSender.stopPlaying()
        //添加设备进SA
        cameraTool = CameraTool()
        let cameraData = CameraModel()
        let camera = CameraCache.getCamera(uid: id)
            cameraData.uid = id
            cameraData.model = model
            cameraData.manufacturer = manufacturer
            cameraData.name = camera.name == "" ? "IPC摄像头IC1" : camera.name
            cameraData.pwd = camera.pwd == "" ? "888888" : camera.pwd
        cameraTool?.cameraData = cameraData
            //连接摄像头
        cameraTool?.connectVideo(uid: cameraData.uid ?? "", pwd: cameraData.pwd)
        
        cameraTool?.completedCallback = { [weak self] camera in
            guard let self = self else { return }
            
            //缓存摄像头数据库
            CameraCache.update(from: camera)

            //注册摄像机
            ApiServiceManager.shared.addCameraDevice( name: camera.name, iid: camera.uid ?? "", model: camera.model, manufacturer: camera.manufacturer, sync_data: camera.toJSONString() ?? "", plugin_id: self.commonDevice?.plugin_id ?? "mhzj") {[weak self] deviceResponse in
                guard let self = self else { return }
                
                if deviceResponse.is_added {//已添加过，直接进入设备详情
                    //检测插件包是否需要更新
                    self.showLoadingView()
                    ApiServiceManager.shared.checkPluginUpdate(id: self.commonDevice?.plugin_id ?? "mhzj") { [weak self] response in
                        guard let self = self else { return }
                        let filepath = ZTZipTool.getDocumentPath() + "/" + (self.commonDevice?.plugin_id ?? "mhzj")
                        @UserDefaultWrapper(key: .plugin(id: self.commonDevice?.plugin_id ?? "mhzj"))
                        var info: String?
                        let cachePluginInfo = Plugin.deserialize(from: info ?? "")
                        //检测本地是否有文件，以及是否为最新版本
                        if ZTZipTool.fileExists(path: filepath) && cachePluginInfo?.version == response.plugin.version {
                            self.hideLoadingView()
//                            let vc = CameraViewController()
//                            vc.myUID = camera.uid ?? ""
//                            vc.mypwd = camera.pwd
//                            vc.deviceID = deviceResponse.device_id
//                            self.navigationController?.pushViewController(vc, animated: true)
//                            if let count = self.navigationController?.viewControllers.count,
//                               count - 2 > 0,
//                               var vcs = self.navigationController?.viewControllers {
//                                vcs.remove(at: count - 2)
//                                self.navigationController?.viewControllers = vcs
//                            }
                            let json = "{ \"device_id\" : \(deviceResponse.device_id),\"status\":0}"
                            callback?(json)
                        }else{
                            //根据路径下载最新插件包，存储在document
                            let downloadUrl = response.plugin.download_url ?? ""
                            ZTZipTool.downloadZipToDocument(urlString: downloadUrl, fileName: response.plugin.id) { [weak self] success in
                                guard let self = self else { return }
                                self.hideLoadingView()
                                if success {
//                                    let vc = CameraViewController()
//                                    vc.myUID = camera.uid ?? ""
//                                    vc.mypwd = camera.pwd
//                                    vc.deviceID = deviceResponse.device_id
//                                    self.navigationController?.pushViewController(vc, animated: true)
//                                    if let count = self.navigationController?.viewControllers.count,
//                                       count - 2 > 0,
//                                       var vcs = self.navigationController?.viewControllers {
//                                        vcs.remove(at: count - 2)
//                                        self.navigationController?.viewControllers = vcs
//                                    }
                                    let json = "{ \"device_id\" : \(deviceResponse.device_id),\"status\":0}"
                                    callback?(json)

                                } else {
                                    self.showToast(string: "下载插件失败".localizedString)
                                }
                                
                            }
                            
                        }
                    }failureCallback: {[weak self] code, err in
                        guard let self = self else { return }
//                        self.showToast(string: err)
                        }
                    }else{//未添加过
                    self.cameraTool?.releaseAll()
                    let vc = DeviceSettingViewController()
                    vc.is_gateway = self.commonDevice?.type == "gateway"
                    vc.area = AuthManager.shared.currentArea
                    vc.device_id = deviceResponse.device_id
                    vc.hidesBottomBarWhenPushed = true
                    self.navigationController?.pushViewController(vc, animated: true)
                    
                    if let count = self.navigationController?.viewControllers.count,
                       count - 2 > 0,
                       var vcs = self.navigationController?.viewControllers {
                        vcs.remove(at: count - 2)
                        self.navigationController?.viewControllers = vcs
                    }

                }
                
                            
                } failureCallback: {[weak self] code, err in
//                    guard let self = self else { return }
//                    self.showToast(string: err)
                    }

        }
        #endif
    }
    
    @objc func goToCamera(params: Dictionary<String,Any>?){
        guard let params = params,
              let device_id = params["device_id"] as? Int
        else {
            return
        }
        
        #if !(targetEnvironment(simulator))
        cameraTool?.releaseAll()
        let vc = CameraViewController()
            vc.deviceID = device_id
        self.navigationController?.pushViewController(vc, animated: true)
        
        if let count = self.navigationController?.viewControllers.count,
           count - 2 > 0,
           var vcs = self.navigationController?.viewControllers {
            vcs.remove(at: count - 2)
            self.navigationController?.viewControllers = vcs
        }

        #endif
    }
    

    
    @objc func openScan(callBack:((_ response: Any?) -> ())?) {
        #if !(targetEnvironment(simulator))
        let vc = CameraScanViewController()
        vc.hidesBottomBarWhenPushed = true
        vc.cameraCallback = {[weak self] result in
            let json = "{ \"ID\" : \"\(result.ID)\",\"model\" : \"\(self?.commonDevice?.model ?? "")\",\"manufacturer\" : \"\(self?.commonDevice?.manufacturer ?? "")\",\"status\":0}"
            callBack?(json)
        }
        self.navigationController?.pushViewController(vc, animated: true)
        #endif
    }
    
    
    /// Set navigation style from js
    /// - Parameter params:
    ///{
    /// title: navigation title,
    /// color: navigation title color,
    /// background: navigation bar color,
    /// isShow: whether navigation bar is hidden
    ///}
    @objc func setTitle(params: Dictionary<String,Any>) {
        if isEmbeddedPage {
            return
        }
        ZTConsole.log("params:%@",params)
        if let title = params["title"] as? String {
            navigationItem.title = title
        }
        
        if let color = params["color"] as? String {
            navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor(hex: color) ?? .black]

        }
        
        if let background = params["background"] as? String {
            navigationController?.navigationBar.barTintColor = UIColor(hex: background)
            navigationController?.navigationBar.backgroundColor = UIColor(hex: background)

        }
        
        if let isShow = params["isShow"] as? Bool {
            if isShow == false {
                navigationController?.setNavigationBarHidden(true, animated: true)
                webView.snp.remakeConstraints { (make) in
                    make.top.equalToSuperview().offset(Screen.statusBarHeight)
                    make.left.right.bottom.equalToSuperview()
                }
            } else {
                navigationController?.setNavigationBarHidden(false, animated: true)
                webView.snp.remakeConstraints { (make) in
                    make.top.equalToSuperview().offset(Screen.k_nav_height)
                    make.left.right.bottom.equalToSuperview()
                }
            }
            
        }

    }
    
    /// 保存webview内容到相册
    /// - Parameter callBack: 回调
    func savePhoto(callback:((_ response: Any?) -> ())?) {
        UIGraphicsBeginImageContextWithOptions(webView.bounds.size, false, UIScreen.main.scale)
        webView.drawHierarchy(in: webView.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        guard let img = image else { return }
        
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAsset(from: img)
        }, completionHandler: { (isSuccess, error) in
            DispatchQueue.main.async {
                if isSuccess {
                    callback?("{\"status\": 0,\"error\": \"\"}")
                } else {
                    callback?("{\"status\": -1,\"error\": \"保存失败\"}")
                }
            }
        })
    }
    
    /// Get the current network status
    /// - Parameter callBack: status callback to js
    /// - Returns: nil
    func networkType(callBack:((_ response: Any?) -> ())?) {
        var status = ""
        switch NetworkReachabilityManager.default?.status {
        case .none:
            break
        case .unknown:
            break
        case .notReachable:
            status = ""
        case .reachable(let type):
            switch type {
            case .cellular:
                let networkInfo = CTTelephonyNetworkInfo()
                status = "4g"
                let carrierType = networkInfo.serviceCurrentRadioAccessTechnology
                if let carrierTypeName = carrierType?.first?.value {
                    switch carrierTypeName {
                    case CTRadioAccessTechnologyGPRS,
                         CTRadioAccessTechnologyEdge,
                         CTRadioAccessTechnologyCDMA1x:
                        status = "2g"
                    case CTRadioAccessTechnologyWCDMA,
                         CTRadioAccessTechnologyHSDPA,
                         CTRadioAccessTechnologyHSUPA,
                         CTRadioAccessTechnologyCDMAEVDORev0,
                         CTRadioAccessTechnologyCDMAEVDORevA,
                         CTRadioAccessTechnologyCDMAEVDORevB,
                         CTRadioAccessTechnologyeHRPD:
                        status = "3g"
                    case CTRadioAccessTechnologyLTE:
                        status = "4g"
                    default:
                        status = "5g"
                    }
                }
                
                
                
            case .ethernetOrWiFi:
                status = "wifi"
            }
        
        }
        
        let json = "{ \"type\" : \"\(status)\" }"
        callBack?(json)
        
       

       
        
    }
    
    
    /// Get the current user informations
    /// - Parameter callBack: userInfo
    /// - Returns: nil
    func getUserInfo(callBack:((_ response: Any?) -> ())?) {
        let json = "{ \"token\" : \"\(authManager.currentArea.sa_user_token)\", \"userId\" : \"\(authManager.currentArea.sa_user_id)\" }"
        callBack?(json)
    }
    
    
    /// if open in app's webview
    /// - Parameter callBack: true
    /// - Returns: isApp
    func isApp(callBack:((_ response: Any?) -> ())?) {
        callBack?("true")
    }
    
    /// if open in professionEdition
    /// - Parameter callBack: true
    /// - Returns: isProfession
    @objc func isProfession(callBack:((_ response: Any?) -> ())?) {
        let json = "{ \"result\" : false }"
        callBack?(json)
    }
    
    /// 提供当前SA使用host给前端
    /// - Parameter callBack: sa.requestURL
    func getLocalhost(callBack:((_ response: Any?) -> ())?) {
        let json = "{ \"localhost\" : \"\(AuthManager.shared.currentArea.requestURL)\" }"
        callBack?(json)
    }
    
    /// 获取插件包config.json信息
    func getPluginConfig(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        guard let plugin_id = params?["pluginId"] as? String else {
            callback?("{}")
            return
        }
        let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + plugin_id + "/" + "config.json"
        guard let url = URL(string: urlPath),
              let json = try? Data(contentsOf: url),
              let str = String(data: json, encoding: .utf8)?.replacingOccurrences(of: "\n", with: "")
        else {
            callback?("{}")
            return
        }
        
        
        callback?(str)
    }

}

//MARK: 提供给JS调用的配网相关方法
extension WKWebViewController {
    /// 获取配网设备信息
    func getDeviceInfo(callback:((_ response: Any?) -> ())?) {
        let deviceJson = self.commonDevice?.toJSONString() ?? "{}"
        callback?(deviceJson)
    }
    
    /// 获取当前wifi信息
    func getConnectWifi(callback:((_ response: Any?) -> ())?) {
        let ssid = NetworkStateManager.shared.getWifiSSID() ?? ""
        let bssid = NetworkStateManager.shared.getWifiBSSID() ?? ""
        // 0：已连接wifi 1：未连接wifi
        let status = ssid == "" ? 1 : 0
        let json1 = "{ \"status\":\(status),\"wifiName\":\"\(ssid)\",\"BSSID\":\"\(bssid)\",\"error_code\": \"0025\" }"
        callback?(json1)
    }
    
    /// 跳转设备详情子设备设置页
    func rotorDeviceSet(params: Dictionary<String,Any>?) {
        guard let params = params,
              let device_id = params["id"] as? Int
        else {
            return
        }
        self.showLoadingView()
        ApiServiceManager.shared.deviceDetail(area: AuthManager.shared.currentArea, device_id: device_id) { [weak self] response in
            guard let self = self else { return }
            guard
                let control = response.device_info.plugin?.control,
                let plugin_id = response.device_info.plugin?.id
            else {
                self.hideLoadingView()
                self.showToast(string: "获取设备信息失败".localizedString)
                return
            }

            ApiServiceManager.shared.checkPluginUpdate(id: plugin_id) { [weak self] response in
                guard let self = self else { return }
                let filepath = ZTZipTool.getDocumentPath() + "/" + plugin_id
                
                @UserDefaultWrapper(key: .plugin(id: plugin_id))
                var info: String?
                
                let cachePluginInfo = Plugin.deserialize(from: info ?? "")
                
                
                //检测本地是否有文件，以及是否为最新版本
                if ZTZipTool.fileExists(path: filepath) && cachePluginInfo?.version == response.plugin.version {
                    self.hideLoadingView()
                    //直接打开插件包获取信息
                    let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + plugin_id + "/" + control
                    let vc = DeviceSettingViewController()
                    vc.plugin_url = urlPath
                    vc.area = AuthManager.shared.currentArea
                    vc.device_id = device_id
                    vc.is_gateway = self.commonDevice?.type == "gateway"
                    vc.hidesBottomBarWhenPushed = true
                    self.navigationController?.pushViewController(vc, animated: true)
                    if let count = self.navigationController?.viewControllers.count,
                       count - 2 > 0,
                       var vcs = self.navigationController?.viewControllers {
                        vcs.remove(at: count - 2)
                        self.navigationController?.viewControllers = vcs
                    }
                } else {
                    //根据路径下载最新插件包，存储在document
                    let downloadUrl = response.plugin.download_url ?? ""
                    ZTZipTool.downloadZipToDocument(urlString: downloadUrl, fileName: plugin_id) { [weak self] success in
                        guard let self = self else { return }
                        self.hideLoadingView()
                        
                        if success {
                            //根据相对路径打开本地静态文件
                            let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + plugin_id + "/" + control
                            let vc = DeviceSettingViewController()
                            vc.plugin_url = urlPath
                            vc.area = AuthManager.shared.currentArea
                            vc.device_id = device_id
                            vc.is_gateway = self.commonDevice?.type == "gateway"
                            vc.hidesBottomBarWhenPushed = true
                            self.navigationController?.pushViewController(vc, animated: true)
                            //存储插件信息
                            info = response.plugin.toJSONString(prettyPrint:true)
                            
                            if let count = self.navigationController?.viewControllers.count,
                               count - 2 > 0,
                               var vcs = self.navigationController?.viewControllers {
                                vcs.remove(at: count - 2)
                                self.navigationController?.viewControllers = vcs
                            }
                        } else {
                            self.showToast(string: "下载插件失败".localizedString)
                        }
                        
                    }
                    
                }
                
                
                
            } failureCallback: { [weak self] code, err in
                guard let self = self else { return }
                self.hideLoadingView()
                // 跳转设备详情
                let vc = DeviceSettingViewController()
                vc.area = AuthManager.shared.currentArea
                vc.device_id = device_id
                vc.is_gateway = self.commonDevice?.type == "gateway"
                self.navigationController?.pushViewController(vc, animated: true)

                if let count = self.navigationController?.viewControllers.count,
                   count - 2 > 0,
                   var vcs = self.navigationController?.viewControllers {
                    vcs.remove(at: count - 2)
                    self.navigationController?.viewControllers = vcs
                }
            }

        } failureCallback: { [weak self] code, err in
            guard let self = self else { return }
            self.hideLoadingView()
            self.showToast(string: "获取设备信息失败".localizedString)
        }

        

    }

    /// 跳转设备详情子设备
    func rotorDevice(params: Dictionary<String,Any>?) {
        guard let params = params,
              let control = params["control"] as? String,
              let plugin_id = params["plugin_id"] as? String,
              let device_id = params["id"] as? Int
        else {
            return
        }
        //检测插件包是否需要更新
        self.showLoadingView()
        ApiServiceManager.shared.checkPluginUpdate(id: plugin_id) { [weak self] response in
            guard let self = self else { return }
            let filepath = ZTZipTool.getDocumentPath() + "/" + plugin_id
            
            @UserDefaultWrapper(key: .plugin(id: plugin_id))
            var info: String?
            
            let cachePluginInfo = Plugin.deserialize(from: info ?? "")
            
            
            //检测本地是否有文件，以及是否为最新版本
            if ZTZipTool.fileExists(path: filepath) && cachePluginInfo?.version == response.plugin.version {
                self.hideLoadingView()
                //直接打开插件包获取信息
                let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + plugin_id + "/" + control
                let vc = DeviceWebViewController(link: urlPath, device_id: device_id)
                vc.area = AuthManager.shared.currentArea
                vc.hidesBottomBarWhenPushed = true
                self.navigationController?.pushViewController(vc, animated: true)
            } else {
                //根据路径下载最新插件包，存储在document
//                let downloadUrl = "http://192.168.22.120/zhiting/zhiting.zip"
                let downloadUrl = response.plugin.download_url ?? ""
                ZTZipTool.downloadZipToDocument(urlString: downloadUrl, fileName: plugin_id) { [weak self] success in
                    guard let self = self else { return }
                    self.hideLoadingView()
                    
                    if success {
                        //根据相对路径打开本地静态文件
                        let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + plugin_id + "/" + control
                        let vc = DeviceWebViewController(link: urlPath, device_id: device_id)
                        vc.area = AuthManager.shared.currentArea
                        vc.hidesBottomBarWhenPushed = true
                        self.navigationController?.pushViewController(vc, animated: true)
                        //存储插件信息
                        info = response.plugin.toJSONString(prettyPrint:true)
                    } else {
                        self.showToast(string: "下载插件失败".localizedString)
                    }
                    
                }
                
            }
            
            
            
        } failureCallback: { [weak self] code, err in
            guard let self = self else { return }
            self.hideLoadingView()
            let filepath = ZTZipTool.getDocumentPath() + "/" + plugin_id
            if ZTZipTool.fileExists(path: filepath) {
                //直接打开插件包获取信息
                let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + plugin_id + "/" + control
                let vc = DeviceWebViewController(link: urlPath, device_id: device_id)
                vc.area = AuthManager.shared.currentArea
                vc.hidesBottomBarWhenPushed = true
                self.navigationController?.pushViewController(vc, animated: true)
            } else {
                self.showToast(string: "检测插件包是否需要更新失败")
            }
            
        }
        
        
        
    }

    /// 连接设备热点
    func connectDeviceHotspot(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        bluFiTool.udpDeviceTool = nil
        if let commonDevice = commonDevice {
            let discoverDevice = DiscoverDeviceModel()
            discoverDevice.model = commonDevice.model
            discoverDevice.manufacturer = commonDevice.manufacturer
            discoverDevice.name = commonDevice.name
            discoverDevice.plugin_id = commonDevice.plugin_id
            discoverDevice.type = commonDevice.type
            discoverDevice.protocol = commonDevice.protocol
            softAPTool.discoverDeviceModel = discoverDevice
        }

        var json = ""
        guard let params = params,
              let ssid = params["hotspotName"] as? String
        else {
            json = "{\"status\": 1,\"error\": \"热点名称为空\", \"error_code\": \"0021\"}"
            DispatchQueue.main.async {
                callBack?(json)
            }
            return
        }
        
        softAPTool.applyConfiguration(ssid: ssid) { isSuccess in
            if isSuccess {
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
                ZTConsole.log("连接热点成功")
            } else {
                json = "{\"status\": 1,\"error\": \"连接热点失败\", \"error_code\": \"0020\"}"
                ZTConsole.log("连接热点失败")
            }
            DispatchQueue.main.async {
                callBack?(json)
            }
            
        }
    }
    
    /// 通过设备热点创建设备
    func createDeviceByHotspot(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        var json = ""
        guard let params = params,
              let deviceName = params["hotspotName"] as? String,
              let pop = params["ownership"] as? String
        else {
            json = "{\"status\": 1,\"error\": \"设备拥有权为空\", \"error_code\": \"0022\"}"
            DispatchQueue.main.async {
                callBack?(json)
            }
            return
        }
        softAPTool.createESPDevice(deviceName: deviceName, proofOfPossession: pop) { device in
            if device != nil { // 创建成功
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
                ZTConsole.log("创建esp设备成功")
            } else { // 失败
                json = "{\"status\": 1,\"error\": \"连接设备失败\", \"error_code\": \"0023\"}"
                ZTConsole.log("创建esp设备失败")
            }
            DispatchQueue.main.async {
                callBack?(json)
            }
        }
    }
    
    /// 通过热点连接设备
    func connectDeviceByHotspot(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        var json = ""
        
        softAPTool.connectESPDevice { status in
            switch status {
            case .connected:
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
                ZTConsole.log("连接esp设备成功")

            case .disconnected:
                json = "{\"status\": 1,\"error\": \"连接设备失败\", \"error_code\": \"0024\"}"
                ZTConsole.log("连接esp设备失败")
                
            case .failedToConnect:
                json = "{\"status\": 1,\"error\": \"连接设备失败\", \"error_code\": \"0024\"}"
                ZTConsole.log("连接esp设备失败")
            }
            DispatchQueue.main.async {
                callBack?(json)
            }
        }
    }
    
    /// 通过热点发送配网信息
    func connectNetworkByHotspot(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        guard let params = params,
              let ssid = params["wifiName"] as? String,
              let pwd = params["wifiPass"] as? String
        else {
            return
        }
        var json = ""
        softAPTool.provisionDevice(ssid: ssid, passphrase: pwd) { status in
            switch status {
            case .success:
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
                
                ZTConsole.log("esp设备配网成功")
                DispatchQueue.main.async {
                    callBack?(json)
                }
                
            case .configApplied:
                ZTConsole.log("esp设备配网中")
                
                
            case .failure:
                json = "{\"status\": 1,\"error\": \"设备配网失败\", \"error_code\": \"0002\"}"
                
                ZTConsole.log("esp设备配网失败")
                DispatchQueue.main.async {
                    callBack?(json)
                }
            }
        }
    }
    
    /// 搜索对应型号配网蓝牙设备
    func scanDeviceByModel(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        softAPTool.udpDeviceTool = nil
        if let commonDevice = commonDevice {
            let discoverDevice = DiscoverDeviceModel()
            discoverDevice.model = commonDevice.model
            discoverDevice.manufacturer = commonDevice.manufacturer
            discoverDevice.name = commonDevice.name
            discoverDevice.plugin_id = commonDevice.plugin_id
            discoverDevice.type = commonDevice.type
            discoverDevice.protocol = commonDevice.protocol
            bluFiTool.discoverDeviceModel = discoverDevice
        }
        guard let params = params,
              let filterContent = params["bluetoothName"] as? String
        else {
            return
        }
        
        bluFiTool.scanBlufiDevices(filterContent: filterContent, callback: { devices in
            let devicesJson = devices
                .map { d -> String in
                    return "{\"uuid\": \"\(d.uuid.uuidString)\",\"name\": \"\(d.name)\"}"
                }
                .joined(separator: ",")

            let json = "{\"status\": 0,\"list\": [\(devicesJson)],\"error\": \"\", \"error_code\": \"\"}"
            callBack?(json)
        })

        
        
    }
    
    /// 通过蓝牙连接设备
    func connectDeviceByBluetooth(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        softAPTool.udpDeviceTool = nil
        guard let params = params else {
            return
        }
        
        var json = ""
        if let commonDevice = commonDevice {
            let discoverDevice = DiscoverDeviceModel()
            discoverDevice.model = commonDevice.model
            discoverDevice.manufacturer = commonDevice.manufacturer
            discoverDevice.name = commonDevice.name
            discoverDevice.plugin_id = commonDevice.plugin_id
            discoverDevice.type = commonDevice.type
            discoverDevice.protocol = commonDevice.protocol
            bluFiTool.discoverDeviceModel = discoverDevice
        }
        bluFiTool.connectCallback = { success in
            if success {
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
                ZTConsole.log("蓝牙设备连接成功")
            } else {
                json = "{\"status\": 1,\"error\": \"蓝牙设备连接失败\", \"error_code\": \"0001\"}"
                ZTConsole.log("蓝牙设备连接失败")
            }
            DispatchQueue.main.async {
                callBack?(json)
            }
        }
        
        if let uuid = params["uuid"] as? String {
            bluFiTool.connectByUUID(uuid: uuid)
            
        } else if let filterContent = params["bluetoothName"] as? String {
            bluFiTool.scanAndConnectDevice(filterContent: filterContent)
        }
        
    }

    /// 通过蓝牙给设备发送配网信息
    func connectNetworkByBluetooth(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        guard let params = params,
              let ssid = params["wifiName"] as? String,
              let pwd = params["wifiPass"] as? String
        else {
            return
        }
        var json = ""
        bluFiTool.provisionCallback = { success in
            if success {
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
            } else {
                json = "{\"status\": 1,\"error\": \"设备配网失败\", \"error_code\": \"0002\"}"
            }
            
            DispatchQueue.main.async {
                callBack?(json)
            }
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
            guard let self = self else { return }
            self.bluFiTool.configWifi(ssid: ssid, pwd: pwd)
        }
        

    }
    
    /// 通过热点发送设备注册
    func registerDeviceByHotspot(callBack:((_ response: Any?) -> ())?) {
        /// 注册设备回调
        softAPTool.registerDeviceCallback = { [weak self] success, error, error_code, device_id, control in
            guard let self = self else { return }
            self.bluFiTool.cancellables.forEach { $0.cancel() }
            self.softAPTool.cancellables.forEach { $0.cancel() }
            var json = ""
            if success {
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
                callBack?(json)
                
            } else {
                json = "{\"status\": 1,\"error\": \"\(error ?? "设备注册失败")\", \"error_code\": \"\(error_code ?? -1)\"}"
                callBack?(json)
            }


            if success {
                guard let device = self.commonDevice ,let device_id = device_id, let control = control else { return }

                //检测插件包是否需要更新
                self.showLoadingView()
                ApiServiceManager.shared.checkPluginUpdate(id: device.plugin_id) { [weak self] response in
                    guard let self = self else { return }
                    let filepath = ZTZipTool.getDocumentPath() + "/" + device.plugin_id
                    
                    @UserDefaultWrapper(key: .plugin(id: device.plugin_id))
                    var info: String?
                    
                    let cachePluginInfo = Plugin.deserialize(from: info ?? "")
                    
                    
                    //检测本地是否有文件，以及是否为最新版本
                    if ZTZipTool.fileExists(path: filepath) && cachePluginInfo?.version == response.plugin.version {
                        self.hideLoadingView()
                        //直接打开插件包获取信息
                        let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + device.plugin_id + "/" + control
                        let vc = DeviceSettingViewController()
                        vc.plugin_url = urlPath
                        vc.area = AuthManager.shared.currentArea
                        vc.device_id = device_id
                        vc.is_gateway = self.commonDevice?.type == "gateway"
                        vc.hidesBottomBarWhenPushed = true
                        self.navigationController?.pushViewController(vc, animated: true)
                        if let count = self.navigationController?.viewControllers.count,
                           count - 2 > 0,
                           var vcs = self.navigationController?.viewControllers {
                            vcs.remove(at: count - 2)
                            self.navigationController?.viewControllers = vcs
                        }
                    } else {
                        //根据路径下载最新插件包，存储在document
        //                let downloadUrl = "http://192.168.22.120/zhiting/zhiting.zip"
                        let downloadUrl = response.plugin.download_url ?? ""
                        ZTZipTool.downloadZipToDocument(urlString: downloadUrl, fileName: device.plugin_id) { [weak self] success in
                            guard let self = self else { return }
                            self.hideLoadingView()
                            
                            if success {
                                //根据相对路径打开本地静态文件
                                let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + device.plugin_id + "/" + control
                                let vc = DeviceSettingViewController()
                                vc.plugin_url = urlPath
                                vc.area = AuthManager.shared.currentArea
                                vc.device_id = device_id
                                vc.is_gateway = self.commonDevice?.type == "gateway"
                                vc.hidesBottomBarWhenPushed = true
                                self.navigationController?.pushViewController(vc, animated: true)
                                //存储插件信息
                                info = response.plugin.toJSONString(prettyPrint:true)
                                
                                if let count = self.navigationController?.viewControllers.count,
                                   count - 2 > 0,
                                   var vcs = self.navigationController?.viewControllers {
                                    vcs.remove(at: count - 2)
                                    self.navigationController?.viewControllers = vcs
                                }
                            } else {
                                self.showToast(string: "下载插件失败".localizedString)
                            }
                            
                        }
                        
                    }
                    
                    
                    
                } failureCallback: { [weak self] code, err in
                    guard let self = self else { return }
                    self.hideLoadingView()
                    // 跳转设备详情
                    let vc = DeviceSettingViewController()
                    vc.is_gateway = self.commonDevice?.type == "gateway"
                    vc.area = AuthManager.shared.currentArea
                    vc.device_id = device_id
                    self.navigationController?.pushViewController(vc, animated: true)

                    if let count = self.navigationController?.viewControllers.count,
                       count - 2 > 0,
                       var vcs = self.navigationController?.viewControllers {
                        vcs.remove(at: count - 2)
                        self.navigationController?.viewControllers = vcs
                    }
                }


            }
        }
       
        softAPTool.registerDevice()
    }
    
    /// 通过蓝牙发送设备注册
    func registerDeviceByBluetooth(callBack:((_ response: Any?) -> ())?) {
        /// 注册设备回调
        bluFiTool.registerDeviceCallback = { [weak self] success, error, error_code, device_id, control in
            guard let self = self else { return }
            self.bluFiTool.cancellables.forEach { $0.cancel() }
            self.softAPTool.cancellables.forEach { $0.cancel() }
            var json = ""
            if success {
                json = "{\"status\": 0,\"error\": \"\", \"error_code\": \"\"}"
                callBack?(json)
                
            } else {
                json = "{\"status\": 1,\"error\": \"\(error ?? "设备注册失败")\", \"error_code\": \"\(error_code ?? -1)\"}"
                callBack?(json)
            }

//
            if success {
                guard let device = self.commonDevice,
                      let device_id = device_id,
                      let control = control,
                      self.bluFiTool.isMultipleProvisioning == false
                else { return }

                //检测插件包是否需要更新
                self.showLoadingView()
                ApiServiceManager.shared.checkPluginUpdate(id: device.plugin_id) { [weak self] response in
                    guard let self = self else { return }
                    let filepath = ZTZipTool.getDocumentPath() + "/" + device.plugin_id
                    
                    @UserDefaultWrapper(key: .plugin(id: device.plugin_id))
                    var info: String?
                    
                    let cachePluginInfo = Plugin.deserialize(from: info ?? "")
                    
                    
                    //检测本地是否有文件，以及是否为最新版本
                    if ZTZipTool.fileExists(path: filepath) && cachePluginInfo?.version == response.plugin.version {
                        self.hideLoadingView()
                        //直接打开插件包获取信息
                        let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + device.plugin_id + "/" + control
                        let vc = DeviceSettingViewController()
                        vc.plugin_url = urlPath
                        vc.area = AuthManager.shared.currentArea
                        vc.device_id = device_id
                        vc.is_gateway = self.commonDevice?.type == "gateway"
                        vc.hidesBottomBarWhenPushed = true
                        self.navigationController?.pushViewController(vc, animated: true)
                        if let count = self.navigationController?.viewControllers.count,
                           count - 2 > 0,
                           var vcs = self.navigationController?.viewControllers {
                            vcs.remove(at: count - 2)
                            self.navigationController?.viewControllers = vcs
                        }
                    } else {
                        //根据路径下载最新插件包，存储在document
        //                let downloadUrl = "http://192.168.22.120/zhiting/zhiting.zip"
                        let downloadUrl = response.plugin.download_url ?? ""
                        ZTZipTool.downloadZipToDocument(urlString: downloadUrl, fileName: device.plugin_id) { [weak self] success in
                            guard let self = self else { return }
                            self.hideLoadingView()
                            
                            if success {
                                //根据相对路径打开本地静态文件
                                let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + device.plugin_id + "/" + control
                                let vc = DeviceSettingViewController()
                                vc.plugin_url = urlPath
                                vc.is_gateway = self.commonDevice?.type == "gateway"
                                vc.area = AuthManager.shared.currentArea
                                vc.device_id = device_id
                                vc.hidesBottomBarWhenPushed = true
                                self.navigationController?.pushViewController(vc, animated: true)
                                //存储插件信息
                                info = response.plugin.toJSONString(prettyPrint:true)
                                if let count = self.navigationController?.viewControllers.count,
                                   count - 2 > 0,
                                   var vcs = self.navigationController?.viewControllers {
                                    vcs.remove(at: count - 2)
                                    self.navigationController?.viewControllers = vcs
                                }
                            } else {
                                self.showToast(string: "下载插件失败".localizedString)
                            }
                            
                        }
                        
                    }
                    
                    
                    
                } failureCallback: { [weak self] code, err in
                    guard let self = self else { return }
                    self.hideLoadingView()
                    // 跳转设备详情
                    let vc = DeviceSettingViewController()
                    vc.is_gateway = self.commonDevice?.type == "gateway"
                    vc.area = AuthManager.shared.currentArea
                    vc.device_id = device_id
                    self.navigationController?.pushViewController(vc, animated: true)

                    if let count = self.navigationController?.viewControllers.count,
                       count - 2 > 0,
                       var vcs = self.navigationController?.viewControllers {
                        vcs.remove(at: count - 2)
                        self.navigationController?.viewControllers = vcs
                    }
                }


            }
        }

        bluFiTool.registerDevice()
    }
    
    /// 跳转系统设置页
    func toSystemWlan() {
        if let url = URL(string: "App-Prefs:root=WIFI"), UIApplication.shared.canOpenURL(url) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        }
    }
    
    func getSystemWifiList(callback:((_ response: Any?) -> ())?) {
        let wifiListJson = networkStateManager.getHistoryWifiList().toJSONString() ?? ""
        let json = "{\"status\": 0,\"list\": \(wifiListJson)}"
        callback?(json)
    }

}

// MARK: - JS - Websocket
extension WKWebViewController {
    
    /// 获取websocket地址
    func getSocketAddress(callback:((_ response: Any?) -> ())?) {
        let addr = AppDelegate.shared.appDependency.websocket.currentAddress ?? ""
        let json = "{\"status\": 0,\"address\": \"\(addr)\"}"
        if ztWebsocket == nil {
            ztWebsocket = ZTWebSocket()
        }
        callback?(json)
    }
    
    /// 创建一个websocket连接
    func connectSocket(params: Dictionary<String,Any>?, callBack:((_ response: Any?) -> ())?) {
        guard let params = params,
              let url = params["url"] as? String
        else {
            return
        }
        
        let sa_user_token: String
        if let header = params["header"] as? Dictionary<String, Any>,
           let token = header["token"] as? String {
            sa_user_token = token
        } else {
            sa_user_token = AuthManager.shared.currentArea.sa_user_token
        }
        if ztWebsocket == nil {
            ztWebsocket = ZTWebSocket()
        }
        
        
        ztWebsocket?.setUrl(urlString: url, token: sa_user_token)
        ztWebsocket?.connect()
    }
    
    /// 通过 WebSocket 连接发送数据
    func sendSocketMessage(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        guard let params = params,
              let jsonData = try? JSONSerialization.data(withJSONObject: params, options: []),
              let decoded = String(data: jsonData, encoding: .utf8)
        else {
            return
        }
        ztWebsocket?.writeString(str: decoded)
        ZTConsole.log("h5Websocket发送信息:\n \(decoded)")
        let json = "{\"status\": 0}"
        callback?(json)
    }
    
    /// 监听 WebSocket 连接打开事件
    func onSocketOpen(callback:((_ response: Any?) -> ())?) {
        if ztWebsocket == nil {
            ztWebsocket = ZTWebSocket()
        }
        ztWebsocket?.h5_onSocketOpenCallback.append({
            let json = "{\"status\": 0}"
            callback?(json)
        })
    }
    
    /// 监听 WebSocket 接受到服务器的消息事件
    func onSocketMessage(callback:((_ response: Any?) -> ())?) {
        let cb: ((String) -> ()) = { msg in
            callback?(msg.replacingOccurrences(of: "\"", with: "\\\""))
        }
        ztWebsocket?.h5_onSocketMessageCallback.append(cb)
    }
    
    /// 监听 WebSocket 错误事件
    func onSocketError(callback:((_ response: Any?) -> ())?) {
        ztWebsocket?.h5_onSocketErrorCallback = { err in
            callback?(err)
        }
    }

    /// 监听 WebSocket 连接关闭事件
    func onSocketClose(callback:((_ response: Any?) -> ())?) {
        ztWebsocket?.h5_onSocketCloseCallback = { reason in
            callback?(reason)
        }
    }
    
    /// 关闭 WebSocket 连接
    func closeSocket(callback:((_ response: Any?) -> ())?) {
        ztWebsocket?.disconnect()
        let json = "{\"status\": 0}"
        callback?(json)
    }
}

// MARK: - JS - 蓝牙设备
extension WKWebViewController {
    /// 门锁日志实时回调
    func getLockLogsRealtime(callback:((_ response: Any?) -> ())?) {
        doorLockUtil?.logCallback = { str in
            callback?(str)
        }
    }
    
    /// 获取私有设备列表
    func getBluetoothSupportDevices(callback:((_ response: Any?) -> ())?) {
        let doorLocks = DoorLockUtil
            .getDoorLocks(area_id: AuthManager.shared.currentArea.id)
            .map { $0.toSupportDeviceJson() }
            .joined(separator: ",")
        
        callback?("{\"status\": 0, \"error\": \"\", \"data\": {\"devices\": [\(doorLocks)]}}")

    }
    
    /// 关联私有设备到网关
    func associateBleDevice(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        guard
            let params = params,
            let data = params["data"] as? [String: Any],
            let uuid = data["uuid"] as? String,
            let iid = data["iid"] as? String,
            let model = data["model"] as? String,
            let plugin_id = data["plugin_id"] as? String,
            let gateway_id = data["gateway_id"] as? String
        else {
            return
        }
        
        let syncData = DoorLockUtil.getUploadData(lock_id: uuid)

        ApiServiceManager.shared.associatePrivateDeviceToGateway(iid: iid, model: model, gateway_id: gateway_id, plugin_id: plugin_id, sync_data: syncData) { _ in
            callback?("{\"status\": 0, \"error\": \"\", \"data\": {\"message\": \"设备已添加至网关，数据已同步\"}}")
            DoorLockUtil.deleteDoorLock(lock_id: uuid)

        } failureCallback: { code, err in
            callback?("{\"status\": 0, \"error\": \"\", \"data\": {\"message\": \"设备已添加至网关，数据同步失败\"}}")
            DoorLockUtil.deleteDoorLock(lock_id: uuid)
            
        }

        
    }

    /// 跳转到设置设备位置页
    func toDevicePosition(params: Dictionary<String,Any>?) {
        guard
            let params = params,
            let device_id = params["id"] as? Int,
            let plugin_id = params["plugin_id"] as? String
        else {
            return
        }
        /// 是否第一次打开
        let is_first = params["is_first"] as? Int
        
        showLoadingView()
        ApiServiceManager.shared.deviceDetail(area: AuthManager.shared.currentArea, device_id: device_id) { [weak self] resp in
            guard let self = self else { return }
            self.showLoadingView()
            let control = resp.device_info.plugin?.control ?? ""
            var urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + plugin_id + "/" + control
            if is_first == 1 {
                urlPath = urlPath + "&is_first=1"
            }

            let vc = DeviceSettingViewController()
            vc.is_gateway = self.commonDevice?.type == "gateway"
            vc.plugin_url = urlPath
            vc.area = AuthManager.shared.currentArea
            vc.device_id = device_id
            vc.hidesBottomBarWhenPushed = true
            self.navigationController?.pushViewController(vc, animated: true)
            if let count = self.navigationController?.viewControllers.count,
               count - 2 > 0,
               var vcs = self.navigationController?.viewControllers {
                vcs.remove(at: count - 2)
                self.navigationController?.viewControllers = vcs
            }
        } failureCallback: { [weak self] code, err in
            guard let self = self else { return }
            self.showLoadingView()
            let vc = DeviceSettingViewController()
            vc.is_gateway = self.commonDevice?.type == "gateway"
            vc.area = AuthManager.shared.currentArea
            vc.device_id = device_id
            vc.hidesBottomBarWhenPushed = true
            self.navigationController?.pushViewController(vc, animated: true)
            if let count = self.navigationController?.viewControllers.count,
               count - 2 > 0,
               var vcs = self.navigationController?.viewControllers {
                vcs.remove(at: count - 2)
                self.navigationController?.viewControllers = vcs
            }
        }

        
        
    }

    /// 发现蓝牙设备
    func getBluetoothDevices(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        guard let params = params else {
            return
        }
        if doorLockUtil == nil {
            doorLockUtil = DoorLockUtil()
        }
        
        /// 多少秒后上报发现列表
        let second = params["second"] as? Int ?? 15
        /// 需上报的设备型号
        let model = params["model"] as? String ?? "MH-"
        
        

        doorLockUtil?.scan(seconds: second, scanFilter: model, callback: { devices in
            let devicesJson = devices
                .map { device -> String in
                    return "{\"iid\": \"\(device.uuid)\", \"with_pin\": \(device.needEncode), \"model\": \"\(device.model)\", \"name\": \"\(device.model)\", \"pair\": \"\(device.pair)\"}"
                }
                .joined(separator: ",")
            
            let resultJson = "{\"status\": 0, \"error\": \"\", \"data\": {\"devices\": [\(devicesJson)]}}"
            callback?(resultJson)
        })

    }
    
    /// 连接蓝牙设备
    func connectBluetoothDevice(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        guard let params = params else {
            return
        }
        if doorLockUtil == nil {
            doorLockUtil = DoorLockUtil()
        }
        
        /// 设备uuid
        guard let uuid = params["iid"] as? String else {
            callback?("{\"status\": -1, \"error\": \"设备id不存在\"}")
            return
        }

        doorLockUtil?.connect(uuid: uuid, callback: { result in
            callback?(result)
        })

    }
    
    /// 蓝牙连接状态
    func getBluetoothState(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        doorLockUtil?.connectstateCallback = { state in
            callback?("{\"status\": 0, \"error\": \"\", \"data\": {\"status\": \(state)}}")
        }
        
        callback?("{\"status\": 0, \"error\": \"\", \"data\": {\"status\": \(doorLockUtil?.doorLock?.peripheral?.state == .connected ? 0 : 1)}}")

    }
    
    func sendMessageByBluetooth(params: Dictionary<String,Any>?, callback:((_ response: Any?) -> ())?) {
        guard
            let params = params,
            let method = params["method"] as? String
        else {
            callback?("{\"status\": -1, \"error\": \"发送失败\"}")
            return
        }
        ZTConsole.log("sendMessageByBluetooth - (\(method))")
        
        /// 设置PIN码
        if method == "pinConnect" {
            guard
                let data = params["data"] as? [String: Any],
                let pin = data["pin"] as? String
            else {
                callback?("{\"status\": 0, \"error\": \"\"}")
                return
            }
            
            doorLockUtil?.setPin(pin: pin, plugin_id: commonDevice?.plugin_id, callback: { [weak self] result in
                guard let self = self else { return }
                callback?(result)
                if result == "{\"status\": 0, \"error\": \"\"}" { /// 成功
                    if let device = self.doorLockUtil?.doorLock?.toDevice() {
                        let urlPath = "file://" + ZTZipTool.getDocumentPath() + "/" + device.plugin_id + "/" + device.control + "&is_first=1"
                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                            let vc = PrivateDeviceWebViewController(link: urlPath, privateDevice: device)
                            vc.area = AuthManager.shared.currentArea
                            vc.hidesBottomBarWhenPushed = true
                            self.navigationController?.pushViewController(vc, animated: true)
                            
                            if let vcs = self.navigationController?.viewControllers,
                               vcs.count > 2,
                               let first = vcs.first,
                               let last = vcs.last {
                                self.navigationController?.viewControllers = [first, last]
                            }
                        }
                        
                    }
                }
            })
        } else if method == "getBluetoothState" { /// 蓝牙设备连接状态
            callback?("{\"status\": 0, \"error\": \"\", \"data\": {\"status\": \(doorLockUtil?.doorLock?.peripheral?.state == .connected ? 0 : 1)}}")
        } else if method == "getLockPowerAndName" { /// 获取电量和名称
            doorLockUtil?.sendFrameData(.queryLockBattery, callback: { json in
                callback?(json)
            })
        } else if method == "getLockUsers" { /// 获取门锁用户列表
            if let lock_id = doorLockUtil?.doorLock?.uuid {
               callback?(doorLockUtil?.userList(lock_id: lock_id))
            }
            
        } else if method == "addLockUser" { /// 添加门锁用户
            guard
                let data = params["data"] as? [String: Any],
                let username = data["username"] as? String,
                let role_type = data["role_type"] as? Int,
                let lock_id = doorLockUtil?.doorLock?.uuid
            else {
                callback?("{\"status\": -1, \"error\": \"添加失败\"}")
                return
            }
            
            callback?(doorLockUtil?.addUser(lock_id: lock_id, username: username, role_type: role_type))
        } else if method == "editLockUser" { /// 编辑门锁用户
            guard
                let data = params["data"] as? [String: Any],
                let name = data["name"] as? String,
                let id = data["id"] as? Int,
                let lock_id = doorLockUtil?.doorLock?.uuid
            else {
                callback?("{\"status\": -1, \"error\": \"编辑失败\"}")
                return
            }
            
            callback?(doorLockUtil?.editUser(lock_id: lock_id, user_id: id, name: name))

        } else if method == "getLockUserInfo" { /// 获取用户信息详情
            guard
                let data = params["data"] as? [String: Any],
                let id = data["id"] as? Int,
                let lock_id = data["iid"] as? String
            else {
                callback?("{\"status\": -1, \"error\": \"获取用户信息失败\"}")
                return
            }
            
            callback?(doorLockUtil?.userDetail(lock_id: lock_id, user_id: id))
        } else if method == "delLockUser" { /// 删除门锁用户
            guard
                let data = params["data"] as? [String: Any],
                let id = data["id"] as? Int,
                let lock_id = data["iid"] as? String
            else {
                callback?("{\"status\": -1, \"error\": \"获取用户信息失败\"}")
                return
            }
            
            callback?(doorLockUtil?.deleteUser(lock_id: lock_id, user_id: id))
        } else if method == "getLockVerifications" { /// 获取门锁未绑定验证方式列表
            guard
                let data = params["data"] as? [String: Any],
                let lock_id = doorLockUtil?.doorLock?.uuid
            else {
                callback?("{\"status\": -1, \"error\": \"获取失败\"}")
                return
            }
            
            let number_type = data["number_type"] as? Int
            let role_type = data["role_type"] as? Int
            let json = doorLockUtil?.verificationList(lock_id: lock_id, number_type: number_type, role_type: role_type, bind: false)
            callback?(json)

        } else if method == "addLockVerification" { /// 添加门锁验证方式
            guard
                let data = params["data"] as? [String: Any],
                let password = data["password"] as? String,
                let role_type = data["role_type"] as? Int
            else {
                callback?("{\"status\": -1, \"error\": \"添加失败\"}")
                return
            }
            
            let number_name = data["number_name"] as? String
            
            if role_type == 1 { /// 普通用户
                doorLockUtil?.sendFrameData(.addAnoymousUserRemotely(user: .normal(name: number_name, pwd: password)), callback: { json in
                    callback?(json)
                })
            } else if role_type == 2 { /// 一次性
                doorLockUtil?.sendFrameData(.addAnoymousUserRemotely(user: .oneTime(name: number_name, pwd: password)), callback: { json in
                    callback?(json)
                })
            } else if role_type == 3 { /// 常访客
                if let valid_time = data["valid_time"] as? String {
                    doorLockUtil?.sendFrameData(.addAnoymousUserRemotely(user: .scope(name: number_name, pwd: password, valid_time: valid_time)), callback: { json in
                        callback?(json)
                    })
                } else {
                    callback?("{\"status\": -1, \"error\": \"添加失败\"}")
                }
                
                
            } else if role_type == 4 { /// 胁迫
                doorLockUtil?.sendFrameData(.addAnoymousUserRemotely(user: .threaten(name: number_name, pwd: password)), callback: { json in
                    callback?(json)
                })
            } else {
                callback?("{\"status\": -1, \"error\": \"添加失败\"}")
            }
            
            
        } else if method == "deleteLockVerification" { /// 删除门锁验证方式
            guard
                let data = params["data"] as? [String: Any],
                let number_type = data["number_type"] as? Int,
                let number_id = data["number_id"] as? Int
            else {
                callback?("{\"status\": -1, \"error\": \"删除失败\"}")
                return
            }
            
            if let type = DoorLockUtil.DoorOpenType(rawValue: UInt8(number_type)) {
                doorLockUtil?.sendFrameData(.deleteUser(type: type, id: number_id), callback: { json in
                    callback?(json)
                })
            } else {
                callback?("{\"status\": -1, \"error\": \"删除失败\"}")
            }
            
        } else if method == "bindLockVerification" { /// 绑定门锁验证方式
            guard
                let data = params["data"] as? [String: Any],
                let user_id = data["user_id"] as? Int,
                let verifications = data["verifications"] as? [[String: Any]],
                let lock_id = doorLockUtil?.doorLock?.uuid
            else {
                callback?("{\"status\": -1, \"error\": \"绑定失败\"}")
                return
            }
            
            let v = verifications.compactMap { dict -> (Int, Int)? in
                guard
                    let number_type = dict["number_type"] as? Int,
                    let number_id = dict["number_id"] as? Int
                else {
                    return nil
                }
                return (number_id, number_type)
            }
            
            callback?(doorLockUtil?.bindValidation(lock_id: lock_id, user_id: user_id, verifications: v))

        } else if method == "editLockVerification" { /// 编辑门锁验证方式
            guard
                let data = params["data"] as? [String: Any],
                let name = data["name"] as? String,
                let number_type = data["number_type"] as? Int,
                let number_id = data["number_id"] as? Int,
                let lock_id = doorLockUtil?.doorLock?.uuid
            else {
                callback?("{\"status\": -1, \"error\": \"编辑失败\"}")
                return
            }
            
            callback?(doorLockUtil?.editVerification(lock_id: lock_id, number_type: number_type, number_id: number_id, name: name))
        } else if method == "getLockSettingInfo" { /// 获取门锁设置
            doorLockUtil?.sendFrameData(.queryLockStatus(type: .setting), callback: { json in
                callback?(json)
            })
           
        } else if method == "editLockSettingInfo" { /// 编辑门锁设置
            guard
                let data = params["data"] as? [String: Any],
                let set_data = data["data"] as? Int,
                let set_type = data["set_type"] as? Int
            else {
                callback?("{\"status\": -1, \"error\": \"设置失败\"}")
                return
            }
            
            if set_type == 1 {
                if let type = DoorLockUtil.DoorSettingType.Language.init(rawValue: UInt8(set_data)) {
                    doorLockUtil?.sendFrameData(.setLockSetting(type: .language(type)), callback: { json in
                        callback?(json)
                    })
                } else {
                    callback?("{\"status\": -1, \"error\": \"设置失败\"}")
                }
                
            }
            
            if set_type == 3 {
                if let type = DoorLockUtil.DoorSettingType.Volume.init(rawValue: UInt8(set_data)) {
                    doorLockUtil?.sendFrameData(.setLockSetting(type: .volume(type)), callback: { json in
                        callback?(json)
                    })
                } else {
                    callback?("{\"status\": -1, \"error\": \"设置失败\"}")
                }
            }
            
            if let normally_open = data["normally_open"] as? Bool, normally_open == true {
                doorLockUtil?.sendFrameData(.setAlwaysOpen, callback: { json in
                    callback?(json)
                })
            }
            
            

        } else if method == "getLockLogs" { /// 获取门锁日志列表
            guard
                let doorLockUtil = doorLockUtil,
                let data = params["data"] as? [String: Any],
                let event_type = data["event_types"] as? [Int],
                let lock_id = data["iid"] as? String
            else {
                callback?("{\"status\": -1, \"error\": \"获取失败\"}")
                return
            }
            
            /// 开始时间戳
            let start_at = data["start_at"] as? Int
            /// 结束时间戳
            let end_at = data["end_at"] as? Int
            /// 分页大小
            let size = data["size"] as? Int
            /// 最大的一条日志id
            let index = data["index"] as? Int

            callback?(doorLockUtil.logList(lock_id: lock_id, event_type: event_type, start_at: start_at, end_at: end_at, size: size, index: index))

        } else if method == "unbindVerification" {
            guard
                let data = params["data"] as? [String: Any],
                let number_type = data["number_type"] as? Int,
                let number_id = data["number_id"] as? Int,
                let lock_id = doorLockUtil?.doorLock?.uuid
            else {
                callback?("{\"status\": -1, \"error\": \"删除失败\"}")
                return
            }
            
            callback?(doorLockUtil?.unbindVerification(lock_id: lock_id, number_type: number_type, number_id: number_id))
        }
    }
    
    
    
    /// webview下载文件
    func downloadFile(params: Dictionary<String,Any>?) {
        guard
            let data = params?["data"] as? [String: Any],
            let url = data["url"] as? String,
            let fileName = data["fileName"] as? String
        else {
            showToast(string: "下载失败")
            return
        }
        
        let destination: DownloadRequest.Destination = { _, _ in
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let fileURL = documentsURL
                .appendingPathComponent("下载方案")
                .appendingPathComponent(fileName)

            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }

        ApiServiceManager.shared.downloadUploadService
            .request(.downloadPlugin(url: url, destination: destination)) { [weak self] result in
                guard let self = self else { return }
                switch result {
                case .success:
                    self.showToast(string: "下载成功,已保存至\"文件\"")
                    
                    
                case .failure:
                    self.showToast(string: "下载插件失败")
                    
                }
            }
        
        
    }
    
    /// 跳转到首页
    func jumpToHomeVC(params: Dictionary<String,Any>?) {
        guard params?["page"] as? String == "home" else { return }
        if let viewControllers = self.navigationController?.viewControllers {
            for viewController in viewControllers {
                if viewController is HomeViewController {
                    self.navigationController?.popToViewController(viewController, animated: true)
                }
            }
        }
    }

}
